﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using NEATlib;
using NeuralLib;
using TimbreRecognition.Recognition.Model;

namespace TimbreRecognition.Recognition.NEAT
{
    public class NEATNetworkCreator
    {
        private const double _connectionMutationRate = 0.05;
        private const double _distanceTreshold = 1.2;
        private const int _generationCount = 10;
        private const int _genomesInPopulationCount = 30;
        private const double _neuronsMutationRate = 0.05;
        private const double _newGenomesPercent = 0.3;
        private const double _connectionDisableMutationRate = 0.05;

        private Dictionary<double[], double[]> validationData;

        public ILogger Logger { get; set; }

        public INetwork Create(int inputNumber, int outputNumber, Dictionary<double[], double[]> trainingData, Dictionary<double[], double[]> validationData)
        {
            List<NEATlib.DataItem> dataItems = ToDataItems(trainingData);

            NeuralNetworkInfo networkInfo = new NeuralNetworkInfo(inputNumber, outputNumber);

                var GA = new NeatAlgorithm(new NeatParameters
                {

                    NeuronMutationRate = _neuronsMutationRate,
                    ConnectionMutationRate = _connectionMutationRate,
                    GenerationCount = _generationCount,
                    GenomesCount = _genomesInPopulationCount,
                    DistanceTreshold = _distanceTreshold,
                    NewGenomesPercent = _newGenomesPercent,
                    ConnectionDisableMutationRate = _connectionDisableMutationRate
                },

                networkInfo,

                dataItems, 
                
                validationData
            );
            GA.OneStepPerformed += GA_OneStepPerformed;

            NeatResult result = GA.Run();

            if (Logger != null)
            {
                Logger.Log("Error = " + result.Error.ToString("F4"));
            }

            DisconnectedNetwork network = NeuralNetworkConstructor.NetworkFromGenome(result.BestGenom, networkInfo);

            NEATNetworkWrapper wrapper = new NEATNetworkWrapper(network);

            return wrapper;
        }

        private List<NEATlib.DataItem> ToDataItems(Dictionary<double[], double[]> data)
        {
            return data.Select(keyValuePair =>
                new NEATlib.DataItem()
                {
                    DataSeries = keyValuePair.Key, 
                    ExpectedOutput = keyValuePair.Value
                }).ToList();
        }

        private void GA_OneStepPerformed(object sender, EventArgs e)
        {
            if (Logger != null)
            {
                ProgressChangedEventArgs progressEvent = (ProgressChangedEventArgs)e;
                NeatResult result = (NeatResult) progressEvent.UserState;
                string message = "Round = " + progressEvent.ProgressPercentage + "%";
                if (result != null)
                {
                    message += ", error = " + result.Error.ToString("F10");
                }
                Logger.Log(message);
            }
        }
    }
}
